package com.borazone.kilio.extra;


import java.io.ByteArrayInputStream;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;

import com.jcraft.jzlib.ZInputStream;


public class RomExtractor {

	public final static int swabInt(int v) {
		return (v >>> 24) | (v << 24) | ((v << 8) & 0x00FF0000)
				| ((v >> 8) & 0x0000FF00);
	}

	String srcName;

	RomExtractor(String source) {
		srcName = source;
	}

	FileOutputStream fOut;

	void deflate() throws Exception {
		File outFolder = new File(srcName + ".out");
		
		outFolder.mkdir();
				
		
		RandomAccessFile rFile = new RandomAccessFile( srcName, "r" );

		int header = rFile.readInt();
		if (header != 0x52455331) {
			throw new Exception("Unsupported file type.");
		}
		int unknown1 = swabInt(rFile.readInt());
		int unknown2 = swabInt(rFile.readInt());
		int tailPos = swabInt(rFile.readInt());
		int tailSize = swabInt(rFile.readInt());
		int numofitem = swabInt(rFile.readInt());

		System.out.println("Unknown 1: " + unknown1);
		System.out.println("List Data Compressed: " + unknown2);
		if( unknown2 != 1 ) {
			throw new Exception( "Plain list data structure found." );
		}
		System.out.println("List Data Start Adress: " + tailPos);
		System.out.println("List Data Length: " + tailSize);
		System.out.println("Number of item: " + numofitem);

		rFile.seek( tailPos );
		byte[] deflatedListData = new byte[tailSize];
		rFile.readFully( deflatedListData );
		
		ByteArrayInputStream bain = new ByteArrayInputStream( deflatedListData );
		ZInputStream zin = new ZInputStream( bain );
		
		for( int i = 0; i < numofitem; i++ ) {
			byte[] listBuf = new byte[536];
			zin.read( listBuf );
			addList( listBuf );
		}
		
		// generate folder trees
		for( int i = 0; i < folderList.size(); i++ ) {
			File f = new File( outFolder + "\\" + folderList.get(i) );
			f.mkdirs();
		}
		
		for( int i = 0; i < list.size(); i++ ) {
			ListItem item = list.get(i);
			File file = new File( outFolder + "\\" + item.fileName );
			
			FileOutputStream fout = new FileOutputStream( file );
			rFile.seek( item.startPos );
			
			System.out.println( item.toString() );
			if( item.length != item.length2 ) {
				FileOutputStream tempout = new FileOutputStream( outFolder + "\\" + item.fileName + ".tmp" );
				for( int j = 0; j < item.length; j++ ) {
					tempout.write( rFile.read() );
				}
				tempout.flush();
				tempout.close();
				
				FileInputStream tempin = new FileInputStream( outFolder + "\\" + item.fileName + ".tmp" );
				ZInputStream tempzin = new ZInputStream( tempin );
				
				int tempread = 0;
				while( -1 < ( tempread = tempzin.read() ) ) {
					fout.write( tempread );
				}
				
				tempin.close();
				tempzin.close();
				
				File tempFile = new File( outFolder + "\\" + item.fileName + ".tmp" );
				tempFile.delete();
			} 
			else {
				for( int j = 0; j < item.length; j++ ) {
					fout.write( rFile.read() );
				}
				
			}
			
			fout.flush();
			fout.close();
		}
		
		rFile.close();
	}

	private void addList( byte[] data ) {
		StringBuffer sb = new StringBuffer();
		for( int i = 0; i < 511; i+=2 ) {
			sb.append( (char) data[i] );
		}
		
		String fileName = sb.toString().trim();	
		
		int startPos = 0;
		startPos += ( (int)data[512] << 24 ) & 0xFF000000;
		startPos += ( (int)data[513] << 16 ) & 0x00FF0000;
		startPos += ( (int)data[514] << 8 ) & 0x0000FF00;
		startPos += ( (int)data[515] ) & 0x000000FF;
		
		int length = 0;
		length += ( (int)data[516] << 24 ) & 0xFF000000;
		length += ( (int)data[517] << 16 ) & 0x00FF0000;
		length += ( (int)data[518] << 8 ) & 0x0000FF00;
		length += ( (int)data[519] ) & 0x000000FF;
		
		int length2 = 0;
		length2 += ( (int)data[520] << 24 ) & 0xFF000000;
		length2 += ( (int)data[521] << 16 ) & 0x00FF0000;
		length2 += ( (int)data[522] << 8 ) & 0x0000FF00;
		length2 += ( (int)data[523] ) & 0x000000FF;
		
		byte[] reserved = new byte[] {
				data[525], data[526], data[527], data[528], data[529], data[530]
		};
		
		ListItem item = new ListItem();
		item.fileName = fileName;
		item.startPos = swabInt(startPos);
		item.length = swabInt(length);
		item.length2 = swabInt(length2);
		item.reserved = reserved;
		
		list.add( item );
		
		String folder = getFolder( fileName );
		if( !folderList.contains(folder) ) {
			folderList.add(folder);
		}
	}
	
	String getFolder( String path ) {
		if( path.lastIndexOf( "\\" ) > 0 ) {
			return path.substring(0, path.lastIndexOf("\\") );
		} else {
			return "";
		}
	}
	
	ArrayList<ListItem> list = new ArrayList<ListItem>();
	
	ArrayList<String> folderList = new ArrayList<String>();
	
	class ListItem {
		String fileName;
		int startPos;
		int length;
		int length2;
		byte[] reserved;
		public String toString() {
			if( length != length2 ) {
				return fileName + " in " + startPos + " with " + length + "," + length2 + " bytes.";
			}
			return fileName + " in " + startPos + " with " + length + " bytes.";
		}
	}
	
	public static void main(String[] args) {
		if( args.length != 1 ) {
			System.out.println("Usage: java RomExtractor filename" );
		}
		
		RomExtractor extractor = new RomExtractor(args[0]);

		try {
			extractor.deflate();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
